#ifndef __DEPENDENCE_GENERATOR_HPP__
#define __DEPENDENCE_GENERATOR_HPP__

#include "../support/Threading.hpp"

template<int TASK_ID, int TASKTAGDIM, int INTERTASKDIM, int N>
struct DependenceGenerator {
  // RUNTIME_WRAPPER is a type that holds all the necessary runtime info
  // TAG_TYPE is a type that holds all the necessary tag info
  //   calling methods of the wrapper with the tag should generate depends information
  template<typename RUNTIME_WRAPPER, typename TAG_TYPE, typename...CONDSE, typename...DEPDISTS>
  static void genDeps(RUNTIME_WRAPPER &w, TAG_TYPE &tt,
                      tuple<CONDSE...> &condsE, tuple<DEPDISTS...> const &dists) {
    if (std::get<N>(dists) != 0) {
      if (!std::get<N>(condsE)) {
        tt.m_arr[N-1] -= std::get<N>(dists); // DEP2
        w.depends(tt);
        tt.m_arr[N-1] += std::get<N>(dists); // DEP2
      }
    }
    DependenceGenerator<TASK_ID, TASKTAGDIM, INTERTASKDIM, N-1>::genDeps(w, tt, condsE, dists);
  }
  
  template<typename RUNTIME_WRAPPER, typename TAG_TYPE, typename...CONDSE, typename...DEPDISTS>
  static void genGets(RUNTIME_WRAPPER &w, TAG_TYPE &this_tt,
                      tuple<CONDSE...> &condsE, tuple<DEPDISTS...> const &dists,
                      void *& wA) {
    if (std::get<N>(dists) != 0 && !w.markedAsyncGet(this_tt, N-1)) {
      if (!std::get<N>(condsE)) {
        TAG_TYPE &copy_tt = RUNTIME_WRAPPER::makeAsyncTagCopy(this_tt);
        
#ifdef DEBUG
        SEQUENTIAL_DUMP1("TAG " << this_tt) ;
#endif
        std::get<N-1>(copy_tt.m_arr) -= std::get<N>(dists); // DEP2

#ifdef DEBUG
        SEQUENTIAL_DUMP1("DEPENDS ON " << copy_tt) ;
#endif
        w.markAsyncGet(this_tt, N-1);
        w.get(this_tt, copy_tt, wA);
        
        // copy_tt.m_arr[N-1] += std::get<N>(dists);  // DEP2
      }
    }
    
    DependenceGenerator<TASK_ID, TASKTAGDIM, INTERTASKDIM, N-1>::genGets(w, this_tt, condsE, dists, wA);
  }

  template<typename RUNTIME_WRAPPER, typename...CONDSE>
  static void genZeroGet(RUNTIME_WRAPPER &w, tuple<CONDSE...> &condsE, void *& wA) {
    // If at least one of the conditions is true, this is not the first task
    // and we should get the task "zero" (zero atm, need to rename).
    if (!std::get<N>(condsE)) return;
    DependenceGenerator<TASK_ID, TASKTAGDIM, INTERTASKDIM, N-1>::genZeroGet(w, condsE, wA);
  }

  template<typename RUNTIME_WRAPPER, typename TAG_TYPE, typename...CONDSE>
  static void genZeroDep(RUNTIME_WRAPPER &w, TAG_TYPE &t, tuple<CONDSE...> &condsE) {
    // If at least one of the conditions is true, this is not the first task
    // and we should get the task "zero" (zero atm, need to rename).
    if (!std::get<N>(condsE)) return;
    DependenceGenerator<TASK_ID, TASKTAGDIM, INTERTASKDIM, N-1>::
      genZeroDep(w, t, condsE);
  }
};

template<int TASK_ID, int TASKTAGDIM, int INTERTASKDIM>
struct DependenceGenerator<TASK_ID, TASKTAGDIM, INTERTASKDIM, 0> {
  template<typename RUNTIME_WRAPPER, typename TAG_TYPE, typename...CONDSE, typename...DEPDISTS>
  static void genDeps(RUNTIME_WRAPPER &, TAG_TYPE &, tuple<CONDSE...> &, 
                      tuple<DEPDISTS...> const &) {}

  template<typename RUNTIME_WRAPPER, typename TAG_TYPE, typename...CONDSE, typename...DEPDISTS>
  static void genGets(RUNTIME_WRAPPER &, TAG_TYPE &, tuple<CONDSE...> &, 
                      tuple<DEPDISTS...> const &, void *&) {}

  template<typename RUNTIME_WRAPPER, typename TAG_TYPE, typename...CONDSE>
  static void genZeroGet(RUNTIME_WRAPPER &w, TAG_TYPE &t, tuple<CONDSE...> &, 
                         void *& wA) {
    // Leaf of the recursion, just get
    w.getZero(t, wA);
  }

  template<typename RUNTIME_WRAPPER, typename TAG_TYPE, typename...CONDSE>
  static void genZeroDep(RUNTIME_WRAPPER &w, TAG_TYPE &t, tuple<CONDSE...> &) {
    // Leaf of the recursion, just do the depends
    w.dependsZero(t);
  }
};


#endif // __DEPENDENCE_GENERATOR_HPP__
